#ifndef __MY_LOG_SINK__
#define __MY_LOG_SINK__
#include<memory>
#include<fstream>
#include<iostream>
#include"util.hpp"
#include<cassert>
#include<sstream>
namespace mylog
{
    class LogSink
    {
        public:
            using ptr = std::shared_ptr<LogSink>;
            virtual ~LogSink(){}
            virtual void log(const char* data, size_t len) = 0;
    }; 
    class StdoutSink : public LogSink
    {
        public:
          void log(const char* data,size_t len)
          {
            std::cout.write(data,len);
          }
    };
    class FileSink : public LogSink
    {
        public:
            FileSink(const std::string& pathname):_pathname(pathname)
            {
                Util::File::CreateDirector(Util::File::path(pathname));
                _ofs.open(_pathname,std::ios::binary | std::ios::app);
                assert(_ofs.is_open());
            }
            void log(const char* data,size_t len)
            {
                _ofs.write(data,len);
                assert(_ofs.good());
            }
        private:
            std::string _pathname;
            std::ofstream _ofs;
    };
    class RollBySizeSink : public LogSink
    {
        public:
            RollBySizeSink(const std::string& basename,size_t max_fsize):_basename(basename),_max_fsize(max_fsize),_cur_fsize(0),_count(0)
            {
                Util::File::CreateDirector(Util::File::path(basename));
                std::string pathname = creatNewFile();
                _ofs.open(pathname,std::ios::binary | std::ios::app);
                assert(_ofs.is_open());
            }
            void log(const char* data,size_t len)
            {
                if(_cur_fsize >= _max_fsize)
                {
                    _ofs.close();
                    std::string pathname = creatNewFile();
                    _ofs.open(pathname,std::ios::binary | std::ios::app);
                    assert(_ofs.is_open());
                    _cur_fsize = 0;
                }

                _ofs.write(data,len);
                _cur_fsize+=len;
                assert(_ofs.good());
            }
        private:
            std::string creatNewFile()
            {
                time_t t = Util::Date::GetTime();
                struct tm lt;
                localtime_r(&t,&lt);
                std::stringstream ss;
                ss << _basename;
                ss << lt.tm_year + 1900 << '-';

                if(lt.tm_mon + 1 < 10)
                {
                    ss << "0";

                }
                ss << lt.tm_mon + 1 << '-';
                if(lt.tm_mday < 10)
                {
                    ss << "0";
                }
                ss << lt.tm_mday << '-';
                if(lt.tm_hour < 10)
                {
                    ss << "0";
                }
                ss << lt.tm_hour << ':';
                if(lt.tm_min < 10)
                {
                    ss << "0";
                }
                ss << lt.tm_min << ':';
                if(lt.tm_sec < 10)
                {
                    ss << "0";
                }
                ss << lt.tm_sec;
                ss << "-";
                ss << _count++;
                ss << ".log";
                return ss.str(); 
            }
        private:
            std::string _basename;
            std::ofstream _ofs;
            int _count;
            size_t _max_fsize;
            size_t _cur_fsize;
    };
    enum class TimeGap
    {
        SECOND,
        MINUTH,
        HOUE,
        DAY
    };
    class RollByTimeSink : public LogSink
    {
        public:
            RollByTimeSink(const std::string& basename,TimeGap gap_size):_basename(basename)
            {
                Util::File::CreateDirector(Util::File::path(basename));
                std::string pathname =creatNewFile();
                
                switch (gap_size)
                {
                case TimeGap::SECOND :
                    _gap_size = 1;
                    break;
                    /* code */
                    break;
                case TimeGap::MINUTH :
                    _gap_size = 60;
                    break;

                case TimeGap::HOUE :
                    _gap_size = 3600;
                    break;

                case TimeGap::DAY :
                    _gap_size = 86400;
                    break;

                default:
                    break;
                }
                _cur_gap = getgap();
                _ofs.open(pathname,std::ios::binary | std::ios::app);
                assert(_ofs.is_open());
            }


            void log(const char* data,size_t len)
            {
                size_t newgap = getgap();
                if(_cur_gap != newgap)
                {
                    _ofs.close();
                    std::string pathname = creatNewFile();
                    _ofs.open(pathname,std::ios::binary | std::ios::app);
                    assert(_ofs.is_open());
                    _cur_gap = newgap;
                }

                _ofs.write(data,len);
                assert(_ofs.good());
            }
        private:
            size_t getgap()
            {
                time_t t = Util::Date::GetTime();
                return t / _gap_size;
            }
            std::string creatNewFile()
            {
                time_t t = Util::Date::GetTime();
                struct tm lt;
                localtime_r(&t,&lt);
                std::stringstream ss;
                ss << _basename;
                ss << lt.tm_year + 1900 << '-';

                if(lt.tm_mon + 1 < 10)
                {
                    ss << "0";

                }
                ss << lt.tm_mon + 1 << '-';
                if(lt.tm_mday < 10)
                {
                    ss << "0";
                }
                ss << lt.tm_mday << '-';
                if(lt.tm_hour < 10)
                {
                    ss << "0";
                }
                ss << lt.tm_hour << ':';
                if(lt.tm_min < 10)
                {
                    ss << "0";
                }
                ss << lt.tm_min << ':';
                if(lt.tm_sec < 10)
                {
                    ss << "0";
                }
                ss << lt.tm_sec;
                ss << ".log";
                return ss.str(); 
            }
        private:
            std::string _basename;
            std::ofstream _ofs;
            size_t _gap_size;
            size_t _cur_gap;
    };
    class Factory
    {
        public:
            template<typename T,typename ...Args>
            static LogSink::ptr creat(Args&& ...args)
            {
                return std::make_shared<T>(std::forward<Args>(args)...);
            }
    };
} // namespace mylog


#endif